// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/tools/quic/quic_simple_server_stream.h"

#include <list>
#include <utility>

#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_split.h"
#include "net/quic/quic_bug_tracker.h"
#include "net/quic/quic_flags.h"
#include "net/quic/quic_spdy_stream.h"
#include "net/quic/spdy_utils.h"
#include "net/spdy/spdy_protocol.h"
#include "net/tools/quic/quic_in_memory_cache.h"
#include "net/tools/quic/quic_simple_server_session.h"

using base::StringPiece;
using base::StringToInt;
using std::string;

namespace net {

QuicSimpleServerStream::QuicSimpleServerStream(QuicStreamId id,
    QuicSpdySession* session)
    : QuicSpdyStream(id, session)
    , content_length_(-1)
{
}

QuicSimpleServerStream::~QuicSimpleServerStream() { }

void QuicSimpleServerStream::OnInitialHeadersComplete(bool fin,
    size_t frame_len)
{
    QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len);
    if (!SpdyUtils::ParseHeaders(decompressed_headers().data(),
            decompressed_headers().length(),
            &content_length_, &request_headers_)) {
        DVLOG(1) << "Invalid headers";
        SendErrorResponse();
    }
    MarkHeadersConsumed(decompressed_headers().length());
}

void QuicSimpleServerStream::OnInitialHeadersComplete(
    bool fin,
    size_t frame_len,
    const QuicHeaderList& header_list)
{
    QuicSpdyStream::OnInitialHeadersComplete(fin, frame_len, header_list);
    if (!SpdyUtils::CopyAndValidateHeaders(header_list, &content_length_,
            &request_headers_)) {
        DVLOG(1) << "Invalid headers";
        SendErrorResponse();
    }
    ConsumeHeaderList();
}

void QuicSimpleServerStream::OnTrailingHeadersComplete(bool fin,
    size_t frame_len)
{
    QUIC_BUG << "Server does not support receiving Trailers.";
    SendErrorResponse();
}

void QuicSimpleServerStream::OnTrailingHeadersComplete(
    bool fin,
    size_t frame_len,
    const QuicHeaderList& header_list)
{
    QUIC_BUG << "Server does not support receiving Trailers.";
    SendErrorResponse();
}

void QuicSimpleServerStream::OnDataAvailable()
{
    while (HasBytesToRead()) {
        struct iovec iov;
        if (GetReadableRegions(&iov, 1) == 0) {
            // No more data to read.
            break;
        }
        DVLOG(1) << "Processed " << iov.iov_len << " bytes for stream " << id();
        body_.append(static_cast<char*>(iov.iov_base), iov.iov_len);

        if (content_length_ >= 0 && body_.size() > static_cast<uint64_t>(content_length_)) {
            DVLOG(1) << "Body size (" << body_.size() << ") > content length ("
                     << content_length_ << ").";
            SendErrorResponse();
            return;
        }
        MarkConsumed(iov.iov_len);
    }
    if (!sequencer()->IsClosed()) {
        sequencer()->SetUnblocked();
        return;
    }

    // If the sequencer is closed, then all the body, including the fin, has been
    // consumed.
    OnFinRead();

    if (write_side_closed() || fin_buffered()) {
        return;
    }

    if (request_headers_.empty()) {
        DVLOG(1) << "Request headers empty.";
        SendErrorResponse();
        return;
    }

    if (content_length_ > 0 && static_cast<uint64_t>(content_length_) != body_.size()) {
        DVLOG(1) << "Content length (" << content_length_ << ") != body size ("
                 << body_.size() << ").";
        SendErrorResponse();
        return;
    }

    SendResponse();
}

void QuicSimpleServerStream::PushResponse(
    SpdyHeaderBlock push_request_headers)
{
    if (id() % 2 != 0) {
        QUIC_BUG << "Client initiated stream shouldn't be used as promised stream.";
        return;
    }
    // Change the stream state to emulate a client request.
    request_headers_ = std::move(push_request_headers);
    content_length_ = 0;
    DVLOG(1) << "Stream " << id() << ": Ready to receive server push response.";

    // Set as if stream decompresed the headers and received fin.
    QuicSpdyStream::OnInitialHeadersComplete(/*fin=*/true, 0);
}

void QuicSimpleServerStream::SendResponse()
{
    if (!ContainsKey(request_headers_, ":authority") || !ContainsKey(request_headers_, ":path")) {
        DVLOG(1) << "Request headers do not contain :authority or :path.";
        SendErrorResponse();
        return;
    }

    // Find response in cache. If not found, send error response.
    const QuicInMemoryCache::Response* response = QuicInMemoryCache::GetInstance()->GetResponse(
        request_headers_[":authority"], request_headers_[":path"]);
    if (response == nullptr) {
        DVLOG(1) << "Response not found in cache.";
        SendNotFoundResponse();
        return;
    }

    if (response->response_type() == QuicInMemoryCache::CLOSE_CONNECTION) {
        DVLOG(1) << "Special response: closing connection.";
        CloseConnectionWithDetails(QUIC_NO_ERROR, "Toy server forcing close");
        return;
    }

    if (response->response_type() == QuicInMemoryCache::IGNORE_REQUEST) {
        DVLOG(1) << "Special response: ignoring request.";
        return;
    }

    // Examing response status, if it was not pure integer as typical h2 response
    // status, send error response.
    string request_url = request_headers_[":authority"].as_string() + request_headers_[":path"].as_string();
    int response_code;
    const SpdyHeaderBlock& response_headers = response->headers();
    if (!ParseHeaderStatusCode(response_headers, &response_code)) {
        LOG(WARNING) << "Illegal (non-integer) response :status from cache: "
                     << response_headers.GetHeader(":status") << " for request "
                     << request_url;
        SendErrorResponse();
        return;
    }

    if (id() % 2 == 0) {
        // A server initiated stream is only used for a server push response,
        // and only 200 and 30X response codes are supported for server push.
        // This behavior mirrors the HTTP/2 implementation.
        bool is_redirection = response_code / 100 == 3;
        if (response_code != 200 && !is_redirection) {
            LOG(WARNING) << "Response to server push request " << request_url
                         << " result in response code " << response_code;
            Reset(QUIC_STREAM_CANCELLED);
            return;
        }
    }
    std::list<QuicInMemoryCache::ServerPushInfo> resources = QuicInMemoryCache::GetInstance()->GetServerPushResources(request_url);
    DVLOG(1) << "Found " << resources.size() << " push resources for stream "
             << id();

    if (!resources.empty()) {
        QuicSimpleServerSession* session = static_cast<QuicSimpleServerSession*>(spdy_session());
        session->PromisePushResources(request_url, resources, id(),
            request_headers_);
    }

    DVLOG(1) << "Sending response for stream " << id();
    SendHeadersAndBodyAndTrailers(response->headers().Clone(), response->body(),
        response->trailers().Clone());
}

void QuicSimpleServerStream::SendNotFoundResponse()
{
    DVLOG(1) << "Sending not found response for stream " << id();
    SpdyHeaderBlock headers;
    headers[":status"] = "404";
    headers["content-length"] = base::IntToString(strlen(kNotFoundResponseBody));
    SendHeadersAndBody(std::move(headers), kNotFoundResponseBody);
}

void QuicSimpleServerStream::SendErrorResponse()
{
    DVLOG(1) << "Sending error response for stream " << id();
    SpdyHeaderBlock headers;
    headers[":status"] = "500";
    headers["content-length"] = base::UintToString(strlen(kErrorResponseBody));
    SendHeadersAndBody(std::move(headers), kErrorResponseBody);
}

void QuicSimpleServerStream::SendHeadersAndBody(
    SpdyHeaderBlock response_headers,
    StringPiece body)
{
    SendHeadersAndBodyAndTrailers(std::move(response_headers), body,
        SpdyHeaderBlock());
}

void QuicSimpleServerStream::SendHeadersAndBodyAndTrailers(
    SpdyHeaderBlock response_headers,
    StringPiece body,
    SpdyHeaderBlock response_trailers)
{
    // This server only supports SPDY and HTTP, and neither handles bidirectional
    // streaming.
    if (!reading_stopped()) {
        StopReading();
    }

    // Send the headers, with a FIN if there's nothing else to send.
    bool send_fin = (body.empty() && response_trailers.empty());
    DVLOG(1) << "Writing headers (fin = " << send_fin
             << ") : " << response_headers.DebugString();
    WriteHeaders(std::move(response_headers), send_fin, nullptr);
    if (send_fin) {
        // Nothing else to send.
        return;
    }

    // Send the body, with a FIN if there's nothing else to send.
    send_fin = response_trailers.empty();
    DVLOG(1) << "Writing body (fin = " << send_fin
             << ") with size: " << body.size();
    WriteOrBufferData(body, send_fin, nullptr);
    if (send_fin) {
        // Nothing else to send.
        return;
    }

    // Send the trailers. A FIN is always sent with trailers.
    DVLOG(1) << "Writing trailers (fin = true): "
             << response_trailers.DebugString();
    WriteTrailers(std::move(response_trailers), nullptr);
}

const char* const QuicSimpleServerStream::kErrorResponseBody = "bad";
const char* const QuicSimpleServerStream::kNotFoundResponseBody = "file not found";

} // namespace net
